Skip to content

astgen: cancel a type write TypeScript cannot bring to an end - #25

Merged
prabhu merged 1 commit into
mainfrom
fix/type-write-budget
Oct 26, 2026
Merged

prabhu merged 1 commit into
mainfrom
fix/type-write-budget

Conversation

@prabhu

@prabhu prabhu commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

astgen ran out of memory on node-express-realworld-example-app and exited without writing an AST for most files. chen then built an atom with almost nothing in it, and every reachables flow for the project disappeared.

The cause is in TypeScript's node builder. Once a write passes astgen's 4096-character limit the builder marks itself as truncating, but in several places it keeps going regardless: substitution, conditional and template literal types are still expanded. Prisma 4's client result types recurse through exactly those. Writing the type of one prisma.article.findMany reached 17 million characters over about 300,000 builder calls and used up 4 GB.

The builder asks the checker's cancellation token once per node it builds, so each write now runs under runWithCancellationToken with a token that cancels after 16,384 calls. A cancelled write is handled exactly like one that ran over the length limit: astgen tries the alias name, then falls back to any. After a cancellation I checked the checker's instantiation depth, active mapper stack and resolution stack, and all three are back at zero, so later writes aren't affected.

On the budget: I ran main and this branch over 37 projects, including juice-shop, taxonomy and the Angular, React, Vue, Svelte and Solid realworld apps. Every AST and type map came out byte for byte the same, no write was cancelled, and no written type needed more than 305 builder calls. On node-express-realworld, main dies after 7 files; this branch writes all 28 in 3 seconds.

ASTGEN_TYPE_TIMING now also reports, per file, the most builder calls a written type needed and how many writes were cancelled.

The new astgen-recursive-types-regression.js uses a small fixture in the same shape as an ORM client's result types. It's my own code, not Prisma's. Without the change the fixture needs about 2 GB; the test runs it under 512 MB, where main fails and this branch passes. The full npm test passes.

ASTGEN_VERSION goes to 4.5.2, because chen keys its parse cache on it and would otherwise keep reusing the empty parse from the crash. The package version is left for the release.

Once it is truncating, TypeScript's node builder still expands substitution,
conditional and template literal types, so a type that recurses through
them runs on until the heap is gone. One prisma.article.findMany (Prisma
4.16) in node-express-realworld-example-app reached 17 million characters
over 300,000 builder calls and took 4 GB with it; astgen exited without an
AST for any file. The builder asks the checker's cancellation token once
per node, so a write now runs under a token that cancels past 16384 calls,
and is then a type too long to write, as when the length limit holds.

Across 37 projects no written type needed more than 305 builder calls, and
every AST and type map is byte for byte as before. ASTGEN_TYPE_TIMING
reports the most a file's written types needed and the writes cancelled.
astgen 4.5.2, so an empty parse cached from the crash is not reused.

Signed-off-by: Team AppThreat <cloud@appthreat.com>
@prabhu prabhu added bug Something isn't working js ai-auto A person set the task, and AI agents carried it through to completion labels Oct 26, 2026
@prabhu
prabhu enabled auto-merge October 26, 2026 03:24
@prabhu
prabhu added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 5a513b9 Oct 26, 2026
3 checks passed
@prabhu
prabhu deleted the fix/type-write-budget branch October 26, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-auto A person set the task, and AI agents carried it through to completion bug Something isn't working js

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant